package com.totoro.rpc.service;

import com.sun.istack.internal.NotNull;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import lombok.AccessLevel;
import lombok.AllArgsConstructor;
import lombok.RequiredArgsConstructor;

import java.io.File;
import java.io.IOException;
import java.io.Serializable;
import java.lang.annotation.Annotation;
import java.net.JarURLConnection;
import java.net.URL;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.List;
import java.util.Map;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;

/**
 * RPCService处理类，用于将RPC注册到服务管理中心
 * Created by Niki on 2018/5/7 16:26
 */
@RequiredArgsConstructor()
@AllArgsConstructor(access = AccessLevel.PROTECTED)
public class RPCServiceHandler extends SimpleChannelInboundHandler {
   @NotNull private Map<String, Class<?>> services ;

    /* 扫描指定的包路径，多个以逗号分隔
     * 用以找出指定包下的@RPCService注解类，对外暴露提供服务
     */
    private static String[] getServicePackages() {
        //先写死com.totoro.rpc.common。模拟配置文件中配置的service包
        //这样扫描某个包下的所有的有RPCService
        return new String[]{"com.totoro.rpc.common"};
    }

    // 找到@RPCService，
    private static List<Class<?>> findService() {
        String[] packages = getServicePackages();
        List<Class<?>> allClass = new ArrayList<Class<?>>();
        for (String packagepath : packages) {
            String packageDirName = packagepath.replace(".", "/");
            Enumeration<URL> dirs;
            try {
                dirs = Thread.currentThread().getContextClassLoader().getResources(packageDirName);
                while (dirs.hasMoreElements()) {
                    URL url = dirs.nextElement();
                    String protocol = url.getProtocol();
                    if ("file".equals(protocol)) {
                        //文件的形式保存在服务器
                        String filePath = url.getPath();
                        findAndAddClassesInPackageByFile(packagepath, filePath, false, allClass);
                    } else if ("jar".equals(protocol)) {
                        //文件以jar包的形式保存在服务器
                        JarFile jar ;
                        jar = ((JarURLConnection) url.openConnection()).getJarFile();
                        Enumeration<JarEntry> entries = jar.entries();

                        while (entries.hasMoreElements()) {
                            //获取jar里的一个实体，可以是目录和jar包里面其他文件
                            JarEntry entry = entries.nextElement();
                            String name = entry.getName();
                            if (name.charAt(0) == '/') {
                                //获取后面的字符串
                                name = name.substring(1);
                            }
                            //如果前半部分和定义的包名相同
                            if (name.startsWith(packageDirName)) {
                                int idx = name.lastIndexOf('/');
                                //如果以“/”结尾，是一个包
                                if (idx != -1) {
                                    //获取包名，
                                    packagepath = name.substring(0, idx).replace('/', '.');
                                }
                                //如果可以迭代下去，并且是一个包
                                if ((idx != -1)) {
                                    if (name.endsWith(".class") && entry.isDirectory()) {
                                        String className = name.substring(packagepath.length()+1, name.length()-6);
                                        try {
                                            allClass.add(Class.forName(packagepath + "." + className));
                                        } catch (ClassNotFoundException e) {
                                            e.printStackTrace();
                                        }
                                    }
                                }
                            }
                        }
                    }
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        return allClass;
    }

    /**
     * 获取路径下所有的class方法
     * @param packageName 包路径
     * @param filePath 文件夹路径
     * @param recursive 是否过滤文件
     * @param allClass 保存calss方法的List
     */
    private static void findAndAddClassesInPackageByFile(String packageName, String filePath, boolean recursive, List<Class<?>> allClass) {
        File dir = new File(filePath);
        if (!dir.exists() || !dir.isDirectory()) {
            return ;
        }

        File[] files = dir.listFiles();
        for (File file : files) {
            if (file.isDirectory()) {
                packageName = packageName + "." + file.getName();
                findAndAddClassesInPackageByFile(packageName, file.getAbsolutePath(), recursive, allClass);
            } else {
                String fileName = file.getName();
                if (fileName.endsWith(".class")) {
                    //仅处理java类文件
                    String className = packageName + "." + fileName.substring(0, fileName.length() - 6);
                    try {
                        Class<?> clazz = Class.forName(className);
                        allClass.add(clazz);

                    } catch (ClassNotFoundException e) {
                        e.printStackTrace();
                        System.out.println("获取"+className+"的类对象异常！");
                    }
                }
            }
        }
    }

    /**
     * 判断class是否有RPCService注解
     * @param clazz
     * @return
     */
    private static boolean checkRpcServiceAnno(Class<?> clazz) {
        if (clazz == null) {
            return false;
        }

        Annotation[] annotations = clazz.getAnnotations();
        if (annotations == null || annotations.length < 1) {
            return false;
        }
        for (Annotation annotation : annotations) {
            if(annotation.annotationType() == RPCService.class){
                return true ;
            }
        }
        return false;
    }


    protected void channelRead0(ChannelHandlerContext channelHandlerContext, Object o) throws Exception {

    }
}
